﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MathNet.Numerics.LinearAlgebra;
using System.Runtime.InteropServices;
using static 摸鱼.全局类;
using OpenCvSharp.Extensions;
using OpenCvSharp;
using static 摸鱼.全局类.SendInputs;
using static 摸鱼.核心;
using static 摸鱼.核心._数据;
using static 摸鱼._数据库._方案._步骤;
using OpenCvSharp.Dnn;
using System.Data;
using System.Windows.Shapes;
using OpenVinoSharp.Extensions.utility;
using OpenVinoSharp.Extensions;
using OpenVinoSharp.preprocess;
using OpenVinoSharp;
using System.Runtime.InteropServices;
using Shape = OpenVinoSharp.Shape;
using System.Security.Cryptography;
using System.Windows.Forms;
using OpenCvSharp.Flann;
using MathNet.Numerics.LinearAlgebra.Factorization;
using static 摸鱼._数据库._方案;
using System.Runtime.CompilerServices;

namespace 摸鱼
{
    public static class 目标检测
    {
        public static void Run(_数据 数据, _步骤 步骤, _方法 方法, _方法data 方法data)
        {
            int[] 区域 = 方法.参数[CS.区域].区域s;
            Rect 截图大小 = new OpenCvSharp.Rect(区域[0], 区域[1], (区域[2] - 区域[0]), (区域[3] - 区域[1]));
            float 相似度 = float.Parse(方法.参数[CS.相似度1].值);
            float 重叠度 = 1 - float.Parse(方法.参数[CS.符合度].值);
            string 变量1 = 方法.参数[CS.变量1].值;
            string 变量2 = 方法.参数[CS.变量2].值;
            string 变量3 = 方法.参数[CS.变量3].值;
            int 类型1 = int.Parse(方法.参数[CS.类型1].值);
            int 类型2 = int.Parse(方法.参数[CS.类型2].值);
            int 类型3 = int.Parse(方法.参数[CS.类型3].值);
            int 框选目标 = int.Parse(数据库.参数[CS.框选目标].值);
            System.Drawing.Point 中心点 = 方法.参数[CS.坐标].坐标;
            string 远近 = 方法.参数[CS.文本1].值;
            bool 取最近 = 远近 == "近" ? true : false;
            float max距离 = -1;
            float min距离 = int.MaxValue;
            float 缩放比例 = 0;
            Queue<int> 类别idq = new();
            Queue<Rect> 位置框q = new();
            List<Rect> 绘图位置框s = new();
            List<int> 绘图类别ids = new();
            bool 第n次 = false;
            Tensor 输出张量 = new Tensor();


            yolov5mode.初始化模型和设备(方法.参数[CS.文件].值, out var 编译模型);
            OpenVinoSharp.InferRequest 推理请求 = 编译模型.create_infer_request();

            while (!方法data.信号.IsSet)
            {
                耗时检测 stop = new(true);
                if (第n次)
                {
                    推理请求.wait();
                    if (编译模型.get_outputs_size() > 1)
                        输出张量 = 推理请求.get_output_tensor(1);
                    else
                        输出张量 = 推理请求.get_output_tensor();

                    yolov5mode.加载图像(ref 缩放比例, ref 截图大小, ref 推理请求);
                    推理请求.start_async();
                }
                else
                {
                    yolov5mode.加载图像(ref 缩放比例, ref 截图大小, ref 推理请求);
                    推理请求.infer();
                    if (编译模型.get_outputs_size() > 1)
                        输出张量 = 推理请求.get_output_tensor(1);
                    else
                        输出张量 = 推理请求.get_output_tensor();

                    yolov5mode.加载图像(ref 缩放比例, ref 截图大小, ref 推理请求);
                    推理请求.start_async();
                    第n次 = true;
                }
                stop.Stop();


                (var 位置框s, var 分数s, var 类ids) = yolov5mode.筛选结果(ref 缩放比例, ref 输出张量);

                // NMS 非最大抑制, 在重叠矩形框中筛选出得分最高的
                int[] 达标Index = new int[位置框s.Count];
                CvDnn.NMSBoxes(位置框s, 分数s, 相似度, 重叠度, out 达标Index);

                for (int i = 0; i < 达标Index.Length; i++)
                {
                    int index = 达标Index[i];
                    var id = 类ids[index];
                    类别idq.Enqueue(id);
                    绘图类别ids.Add(id);

                    var rect = 位置框s[index];
                    位置框q.Enqueue(rect);
                    绘图位置框s.Add(rect);
                }
                if (框选目标 == 1)
                    前景窗口.绘制(绘图位置框s, 绘图类别ids);

                //id , min||max距离
                Dictionary<int, (float 距离, Point point)> 类别距离筛选 = new();
                if (取最近)
                {
                    while (类别idq.TryDequeue(out var 类别res))
                    {
                        if (!类别距离筛选.ContainsKey(类别res))
                        {
                            Rect 距离res = 位置框q.Dequeue();
                            Point res中点 = new(距离res.X + 距离res.Width * 0.5F, 距离res.Y + 距离res.Height * 0.5F);
                            min距离 = (float)直线距离(res中点.X, 中心点.X, res中点.Y, 中心点.Y);
                            类别距离筛选.Add(类别res, (min距离, res中点));
                        }
                        else if (类别距离筛选[类别res].距离 < min距离)
                        {
                            Rect 距离res = 位置框q.Dequeue();
                            Point res中点 = new(距离res.X + 距离res.Width * 0.5F, 距离res.Y + 距离res.Height * 0.5F);
                            min距离 = (float)直线距离(res中点.X, 中心点.X, res中点.Y, 中心点.Y);
                            类别距离筛选[类别res] = (min距离, res中点);
                        }
                    }
                }
                else
                {
                    while (类别idq.TryDequeue(out var 类别res))
                    {
                        if (!类别距离筛选.ContainsKey(类别res))
                        {
                            Rect 距离res = 位置框q.Dequeue();
                            Point res中点 = new(距离res.X + 距离res.Width * 0.5F, 距离res.Y + 距离res.Height * 0.5F);
                            max距离 = (float)直线距离(res中点.X, 中心点.X, res中点.Y, 中心点.Y);
                            类别距离筛选.Add(类别res, (max距离, res中点));
                        }
                        else if (类别距离筛选[类别res].距离 > max距离)
                        {
                            Rect 距离res = 位置框q.Dequeue();
                            Point res中点 = new(距离res.X + 距离res.Width * 0.5F, 距离res.Y + 距离res.Height * 0.5F);
                            max距离 = (float)直线距离(res中点.X, 中心点.X, res中点.Y, 中心点.Y);
                            类别距离筛选[类别res] = (max距离, res中点);
                        }
                    }
                }
                位置框q.Clear();
                类别idq.Clear();

                if (类别距离筛选.TryGetValue(类型1, out var res1))
                    变量1.赋值($"{res1.point.X},{res1.point.Y}");
                else
                    变量1.赋值("-1");

                if (类别距离筛选.TryGetValue(类型2, out var res2))
                    变量2.赋值($"{res2.point.X},{res2.point.Y}");
                else
                    变量2.赋值("-1");

                if (类别距离筛选.TryGetValue(类型3, out var res3))
                    变量3.赋值($"{res3.point.X},{res3.point.Y}");
                else
                    变量3.赋值("-1");
            }
            方法data.信号.Wait();
        }


    }
    public static class yolov9mode
    {

        public unsafe static (List<Rect> 位置框s, List<float> 分数s, List<int> 类ids) 筛选结果(ref float 缩放比例, ref Tensor 输出张量)
        {
            int output_length = (int)输出张量.get_size();
            // -------- Step 9. 工艺要求  --------
            Span<float> output_data = 输出张量.get_data<float>(output_length);
            Mat 结果数据 = new Mat(84, 8400, MatType.CV_32F);
            for (int i = 0; i < 84; i++)
            {
                for (int j = 0; j < 8400; j++)
                {
                    int index = i * 8400 + j;
                    结果数据.Set<float>(i, j, output_data[index]);
                }
            }


            结果数据 = 结果数据.T();

            // 存储结果列表
            List<Rect> 位置框s = new List<Rect>();
            List<int> 类ids = new List<int>();
            List<float> 分数s = new List<float>();
            // 预处理输出结果
            for (int i = 0; i < 结果数据.Rows; i++)
            {
                Mat 类别分数s = new Mat(结果数据, new Rect(4, i, 80, 1));
                OpenCvSharp.Point 最高分id位置, 最低分id位置;
                double 最大得分, 最小得分;
                // 获取最高分在类别数据中的位置
                Cv2.MinMaxLoc(类别分数s, out 最小得分, out 最大得分, out 最低分id位置, out 最高分id位置);
                // 置信水平介于 0 ~ 1
                if (最大得分 > 0.25)
                {
                    float cx = 结果数据.At<float>(i, 0);
                    float cy = 结果数据.At<float>(i, 1);
                    float ow = 结果数据.At<float>(i, 2);
                    float oh = 结果数据.At<float>(i, 3);
                    int x = (int)((cx - 0.5 * ow) * 缩放比例);
                    int y = (int)((cy - 0.5 * oh) * 缩放比例);
                    int width = (int)(ow * 缩放比例);
                    int height = (int)(oh * 缩放比例);
                    Rect 位置框item = new();
                    位置框item.X = x;
                    位置框item.Y = y;
                    位置框item.Width = width;
                    位置框item.Height = height;

                    位置框s.Add(位置框item);
                    类ids.Add(最高分id位置.X);
                    分数s.Add((float)最大得分);
                }
            }
            return (位置框s, 分数s, 类ids);
        }

        public static void 初始化模型和设备(string 模型文件, out CompiledModel 编译模型)
        {
            // -------- Step 1. 初始化 OpenVINO 运行时内核 --------
            OpenVinoSharp.Core 处理核心 = new Core();
            // -------- Step 2. 读取推理模型 --------
            Model 模型 = 处理核心.read_model(模型文件);
            OvExtensions.printf_model_info(模型);
            //预处理模型, 能略微提升性能?
            PrePostProcessor 处理器 = new PrePostProcessor(模型);
            Tensor input_张量_pro = new Tensor(new OvType(ElementType.U8), new OpenVinoSharp.Shape(1, 1800, 1800, 3));
            InputInfo input_信息 = 处理器.input(0);
            InputTensorInfo input_张量信息 = input_信息.tensor();
            input_张量信息.set_from(input_张量_pro).set_layout(new Layout("NHWC")).set_color_format(ColorFormat.BGR);
            PreProcessSteps process_steps = input_信息.preprocess();

            process_steps.convert_color(ColorFormat.RGB).resize(ResizeAlgorithm.RESIZE_LINEAR)
                .convert_element_type(new OvType(ElementType.F32)).scale(255.0f).convert_layout(new Layout("NCHW"));

            Model 新模型 = 处理器.build();
            // -------- Step 3. 将模型加载到设备 --------
            Dictionary<string, string> properties = new() { { "PERFORMANCE_HINT", "LATENCY" } };
            //编译模型 = 处理核心.compile_model(新模型, "BATCH:CPU");
            编译模型 = 处理核心.compile_model(新模型, "CPU");
        }
        public static void 加载图像(ref float 缩放比例, ref Rect 区域, ref InferRequest 推理请求)
        {
            Mat 图像c4 = new OpenCvSharp.Mat(截屏.图片[0, imageType.Mat], new Rect(0, 0, 1920, 1080));
            Mat 图像c3 = new Mat(图像c4.Size(), MatType.CV_8UC3);
            Cv2.CvtColor(图像c4, 图像c3, ColorConversionCodes.BGRA2BGR);

            int 图像maxLength = 图像c3.Cols > 图像c3.Rows ? 图像c3.Cols : 图像c3.Rows;
            Mat 图像Resize = Mat.Zeros(new OpenCvSharp.Size(图像maxLength, 图像maxLength), MatType.CV_8UC3);
            Rect roi = new Rect(0, 0, 图像c3.Cols, 图像c3.Rows);
            图像c3.CopyTo(new Mat(图像Resize, roi));
            float factor = (float)(图像maxLength / 640F);

            // -------- Step 6. Set up input data --------

            Tensor input_tensor = 推理请求.get_input_tensor();
            Shape input_shape = input_tensor.get_shape();
            Mat input_mat = CvDnn.BlobFromImage(图像Resize, 1.0 / 255.0, new OpenCvSharp.Size(input_shape[2], input_shape[3]), new(0), true, false);
            float[] input_data = new float[input_shape[1] * input_shape[2] * input_shape[3]];
            Marshal.Copy(input_mat.Ptr(0), input_data, 0, input_data.Length);
            input_tensor.set_data<float>(input_data);
        }

    }
    public static class yolov5mode
    {
        public static void 加载图像(ref float 缩放比例, ref Rect 区域, ref InferRequest 推理请求)
        {
            Mat 图像c4 = new OpenCvSharp.Mat(截屏.图片[0, imageType.Mat], 区域);
            Mat 图像c3 = new Mat(图像c4.Size(), MatType.CV_8UC3);
            Cv2.CvtColor(图像c4, 图像c3, ColorConversionCodes.BGRA2BGR);

            int 图像maxLength = 图像c3.Cols > 图像c3.Rows ? 图像c3.Cols : 图像c3.Rows;
            Mat 图像Resize = Mat.Zeros(new OpenCvSharp.Size(图像maxLength, 图像maxLength), MatType.CV_8UC3);
            Rect roi = new Rect(0, 0, 图像c3.Cols, 图像c3.Rows);
            图像c3.CopyTo(new Mat(图像Resize, roi));
            缩放比例 = (float)(图像maxLength / 640F);

            Tensor input_tensor = 推理请求.get_input_tensor();
            Shape input_shape = input_tensor.get_shape();
            Mat input_mat = CvDnn.BlobFromImage(图像Resize, 1.0 / 255.0, new OpenCvSharp.Size(input_shape[2], input_shape[3]), new(0), true, false);
            float[] input_data = new float[input_shape[1] * input_shape[2] * input_shape[3]];
            Marshal.Copy(input_mat.Ptr(0), input_data, 0, input_data.Length);
            input_tensor.set_data<float>(input_data);
        }

        public static void 初始化模型和设备(string 模型文件, out CompiledModel 编译模型)
        {
            Core core = new Core();
            Model model = core.read_model(模型文件);
            OvExtensions.printf_model_info(model);
            编译模型 = core.compile_model(model, "AUTO", new() { { "PERFORMANCE_HINT", "LATENCY" } });
        }
        public static (List<Rect> 位置框s, List<float> 分数s, List<int> 类ids) 筛选结果(ref float 缩放比例, ref Tensor 输出张量)
        {

            int output_length = (int)输出张量.get_size();
            Span<float> output_data = 输出张量.get_data<float>(output_length);
            Mat 结果数据 = new Mat(84, 8400, MatType.CV_32F);
            for (int i = 0; i < 84; i++)
            {
                for (int j = 0; j < 8400; j++)
                {
                    int index = i * 8400 + j;
                    结果数据.Set<float>(i, j, output_data[index]);
                }
            }

            List<Rect> 位置框s = new List<Rect>();
            List<int> 类别ids = new List<int>();
            List<float> 得分s = new List<float>();
            for (int i = 0; i < 结果数据.Rows; i++)
            {
                Mat 班级分数 = new Mat(结果数据, new Rect(5, i, 80, 1));
                OpenCvSharp.Point 最高分类Id位置, min_classId_point;
                double 最高分, min_score;
                Cv2.MinMaxLoc(班级分数, out min_score, out 最高分, out min_classId_point, out 最高分类Id位置);
                if (最高分 > 0.2)
                {
                    float cx = 结果数据.At<float>(i, 0);
                    float cy = 结果数据.At<float>(i, 1);
                    float ow = 结果数据.At<float>(i, 2);
                    float oh = 结果数据.At<float>(i, 3);
                    float 得分 = 结果数据.At<float>(i, 4);
                    int x = (int)((cx - 0.5 * ow) * 缩放比例);
                    int y = (int)((cy - 0.5 * oh) * 缩放比例);
                    int width = (int)(ow * 缩放比例);
                    int height = (int)(oh * 缩放比例);
                    Rect box = new Rect();
                    box.X = x;
                    box.Y = y;
                    box.Width = width;
                    box.Height = height;

                    位置框s.Add(box);
                    类别ids.Add(最高分类Id位置.X);
                    得分s.Add((float)得分);
                }
            }
            return (位置框s, 得分s, 类别ids);
        }

    }
}

